#pragma once
#include <cstdint>
#include <stdlib.h>
#include <cstring>
#include <vector>
#include <map>
#include "ai_common.h"
namespace pointcloud_tools
{

    struct LayerConfig
    {
        int layer_id = 0;
        uint8_t cfg_inch_loop_num = 3;
        uint32_t cfg_start_pt_addr = 0;
        uint8_t cfg_shift_num = 0;
        uint8_t cfg_pe_relu_enable = 0;
        uint8_t cfg_cam_search_range[3] = {1, 1, 1};
        uint8_t cfg_cnn_size[3] = {3, 3, 3};
        uint8_t cfg_bias_enable = 0;
        uint8_t cfg_cam_enable = 0x7;
        uint8_t cfg_core_enable = 0xf;

        int8_t bias_core0[8] = {0, 0, 0, 0, 0, 0, 0, 0};
        int8_t bias_core1[8] = {0, 0, 0, 0, 0, 0, 0, 0};
        int8_t bias_core2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
        int8_t bias_core3[8] = {0, 0, 0, 0, 0, 0, 0, 0};

        uint64_t BASE_WT_ADDR;
        uint16_t DMA_WT_NUM = 4096; // 8*8*4*4*32*8/64 = 4096
        std::vector<uint64_t> wt_data;
    };

    struct BlockConfig
    {
        uint16_t cfg_cam_valid_num = 1024;
        uint16_t cfg_total_point_num = 1024;
        uint64_t BASE_PT_ADDR = 0x95000000;
        uint64_t BASE_IN_ADDR = 0x95000000 + 0x100000; // 1MB for IN
        uint64_t BASE_RS_ADDR = 0x95000000 + 0x200000; // 2MB for RS
        std::vector<uint64_t> pt_data;
        std::vector<uint64_t> in_data;
        std::vector<uint64_t> rs_data;
        uint16_t* spatial_shape = nullptr; // [z_min, y_min, x_min, z_max, y_max, x_max]
        char save_folder[32] = "RunsFiles_offline";
    };

    // Cache for block configs, including the instructions
    struct BlockConfigCache
    {
        static std::map<int, std::vector<uint32_t>> inst_map; // key: block size, value: instructions
    };

    struct Pointcloud_utils
    {
        // Basic PARAM
        uint64_t DDR_BASE_ADDR;
        int ddr_fd;
        char save_folder[32] = "RunsFiles_offline";
        size_t ddrSize = 8 * 1024 * 1024 * sizeof(int); // Simulated DDR SIZE, 32MB
        void *mappedDDR;                                // 映射的数据指针

        // Struct of CFG PARAM
        LayerConfig layer_config;
        std::vector<BlockConfig> block_config;

        void wt_init();      // init weight using weight file and weight instruction file
        void compute_init(); // init compute instruction using layer config and block config
        void run();
        int inst_gen_block(BlockConfig cfg);

        // Simulated Model
        bool Model_DDR_alloc(const char *filename);
        bool Model_DDR_close();
        void Model_RandomDataGenerator();
    };

    // 按pt_reg.h顺序定义APB寄存器结构体
    struct apb_regs_block
    {
        uint32_t TOP_CHOOSE;
        uint32_t Reserve[8];
        uint32_t TOP_START;                      // 0x24
        uint32_t CFG_DMA_MODE;                   // 0x28
        uint32_t CFG_CAM_MODE;                   // 0x2C
        uint32_t CFG_CAM_VALID_NUM;              // 0x30
        uint32_t CFG_TOTAL_PT_NUM;               // 0x34
        uint32_t CFG_START_PT_ADDR;              // 0x38
        uint32_t CFG_SCNN_INCHLOOP;              // 0x3C
        uint32_t CFG_CORE_ENABLE;                // 0x40
        uint32_t CFG_CAM_ENABLE;                 // 0x44
        uint32_t CFG_BIAS_ENABLE;                // 0x48
        uint32_t CFG_CNN_SIZE_X;                 // 0x4C
        uint32_t CFG_CNN_SIZE_Y;                 // 0x50
        uint32_t CFG_CNN_SIZE_Z;                 // 0x54
        uint32_t CFG_CAM_SEARCH_RANGE_1;         // 0x58
        uint32_t CFG_CAM_SEARCH_RANGE_2;         // 0x5C
        uint32_t CFG_CAM_SEARCH_RANGE_3;         // 0x60
        uint32_t CFG_PE_BIAS_IN_CORE0_PE_COL0_3; // 0x64
        uint32_t CFG_PE_BIAS_IN_CORE0_PE_COL4_7; // 0x68
        uint32_t CFG_PE_BIAS_IN_CORE1_PE_COL0_3; // 0x6C
        uint32_t CFG_PE_BIAS_IN_CORE1_PE_COL4_7; // 0x70
        uint32_t CFG_PE_BIAS_IN_CORE2_PE_COL0_3; // 0x74
        uint32_t CFG_PE_BIAS_IN_CORE2_PE_COL4_7; // 0x78
        uint32_t CFG_PE_BIAS_IN_CORE3_PE_COL0_3; // 0x7C
        uint32_t CFG_PE_BIAS_IN_CORE3_PE_COL4_7; // 0x80
        uint32_t CFG_PE_RELU_ENABLE;             // 0x84
        uint32_t CFG_PE_RES_SHIFT_NUM;           // 0x88
        uint32_t CFG_TRANSFER_MODE;              // 0x8C
        uint32_t CFG_RAM_START_ADDR;             // 0x90
        uint32_t CFG_AXI_START_ADDR_32;          // 0x94
        uint32_t CFG_AXI_START_ADDR_40;          // 0x98
        uint32_t CFG_TRANSFER_NUM;               // 0x9C
        uint32_t CFG_TOP_FINISH;                 // 0xA0
        uint32_t CFG_FINISH_CLR;                 // 0xA4
        // 如有更多寄存器可继续补充
    };

    struct Pointcloud_online_utils
    {
        // Basic PARAM
        uint64_t DDR_BASE_ADDR;
        char save_folder[256] = "RunsFiles_online"; // Read result to this folder
        // Struct of CFG PARAM
        LayerConfig layer_config;
        std::vector<BlockConfig> block_config;

        // SRAM空间映射指针
        volatile uint32_t *dma_inst;
        // DDR空间映射指针
        volatile uint64_t *ddrbuf;
        // APB寄存器空间映射指针，类型为结构体指针
        volatile apb_regs_block *apb_regs;

        // 初始化
        int devmem_fd; // /dev/mem 文件描述符
        void pmem_mmap_init();
        // 测试函数
        void sram_rw_test();
        void ddr_rw_test();

        void wt_init();
        void wt_init(const char inst_file[256], const char weight_file[256]); // init weight using weight file and weight instruction file
        void compute_init();                                                  // init compute instruction using layer config and block config
        int inst_gen_block(BlockConfig cfg);
        void run();
        void run_block(int cnn_inst_index);
        void run_block();
        void run(char inst_file[256], char save_folder[256], int core_num);
        int  runblock_inst(BlockConfig cfg);
        void run_block_onebyone(int cnn_inst_index);
        ////////////////////////////////////////////////////
    };

    inline void push_inst(std::vector<uint32_t> *inst_buf, uint32_t inst31_0, uint32_t inst63_32, uint32_t inst95_64, uint32_t inst127_96)
    {
        inst_buf->push_back(inst31_0);
        inst_buf->push_back(inst63_32);
        inst_buf->push_back(inst95_64);
        inst_buf->push_back(inst127_96);
    }

    inline int get_used_core_num(uint8_t cfg_core_enable)
    {
        switch (cfg_core_enable)
        {
        case 1:
            return 1;
        case 3:
            return 2;
        case 7:
            return 3;
        case 15:
            return 4;
        default:
            printf("Invalid cfg_core_enable = %x\n", cfg_core_enable);
            exit(1);
        }
    }
    // online utils should contain the computing instruction genertor
    // instructions can be directly streamed to DDR without file IO
    // Online and Offline tools should be maximally compatitable
}